package com.ikingtech.platform.service.system.role.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.base.model.DragOrderParam;
import com.ikingtech.framework.sdk.context.event.TenantDeleteEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.data.helper.DragHelper;
import com.ikingtech.framework.sdk.data.helper.DragHelperBuilder;
import com.ikingtech.framework.sdk.enums.common.DragTargetPositionEnum;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.role.api.RoleApi;
import com.ikingtech.framework.sdk.role.api.RoleUserApi;
import com.ikingtech.framework.sdk.role.model.RoleDTO;
import com.ikingtech.framework.sdk.role.model.RoleMenuUpdateParamDTO;
import com.ikingtech.framework.sdk.role.model.RoleQueryParamDTO;
import com.ikingtech.framework.sdk.role.model.RoleUserUpdateParamDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.role.entity.RoleDO;
import com.ikingtech.platform.service.system.role.entity.RoleMenuDO;
import com.ikingtech.platform.service.system.role.exception.RoleExceptionInfo;
import com.ikingtech.platform.service.system.role.service.RoleDataScopeRepository;
import com.ikingtech.platform.service.system.role.service.RoleMenuRepository;
import com.ikingtech.platform.service.system.role.service.RoleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
@ApiController(value = "/system/role", name = "系统管理-角色管理", description = "系统管理-角色管理")
public class RoleController implements RoleApi {

    private final RoleRepository service;

    private final RoleDataScopeRepository dataScopeService;

    private final RoleMenuRepository roleMenuService;

    private final RoleUserApi roleUserApi;

    @Override
    @OperationLog(value = "新增角色", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(RoleDTO role) {
        RoleDO entity = Tools.Bean.copy(role, RoleDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setSortOrder(this.service.getMaxSortOrder() + 1);
        entity.setDomainCode(Me.domainCode());
        entity.setTenantCode(Me.tenantCode());
        entity.setAppCode(Me.appCode());
        this.service.save(entity);
        return R.ok(entity.getId());
    }

    @Override
    @OperationLog(value = "删除角色")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        RoleDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        this.service.removeById(id);
        this.dataScopeService.removeByRoleId(id);
        this.roleMenuService.removeByRoleId(id);
        this.roleUserApi.removeUserRole(id);
        return R.ok();
    }

    @Override
    @OperationLog(value = "更新角色")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(RoleDTO role) {
        if (!Boolean.TRUE.equals(this.service.exist(role.getId()))) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        this.service.updateById(Tools.Bean.copy(role, RoleDO.class));
        return R.ok();
    }

    @Override
    public R<List<RoleDTO>> page(RoleQueryParamDTO queryParam) {
        return R.ok(this.service.listPage(queryParam, Me.tenantCode()).convertBatch(this.service::modelConvert));
    }

    @Override
    public R<List<RoleDTO>> all() {
        return R.ok(this.service.modelConvert(this.service.listByDomainCodeAndTenantCodeAndAppCode(Me.domainCode(), Me.tenantCode(), Me.appCode())));
    }

    @Override
    public R<RoleDTO> detail(String id) {
        RoleDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        return R.ok(this.service.modelConvert(entity, this.dataScopeService.listDataScopeCodeByRoleId(entity.getId())));
    }

    @Override
    @OperationLog(value = "更新角色菜单")
    public R<Object> updateRoleMenu(RoleMenuUpdateParamDTO roleMenu) {
        this.roleMenuService.removeByRoleId(roleMenu.getRoleId());
        if (Tools.Coll.isNotBlank(roleMenu.getMenuIds())) {
            this.roleMenuService.saveBatch(Tools.Coll.convertList(roleMenu.getMenuIds(), menuId -> {
                RoleMenuDO entity = new RoleMenuDO();
                entity.setId(Tools.Id.uuid());
                entity.setRoleId(roleMenu.getRoleId());
                entity.setMenuId(menuId);
                entity.setDomainCode(Me.domainCode());
                entity.setTenantCode(Me.tenantCode());
                entity.setAppCode(Me.appCode());
                return entity;
            }));
        }
        return R.ok();
    }

    @Override
    @OperationLog(value = "分配角色用户")
    public R<Object> addRoleUser(RoleUserUpdateParamDTO updateParam) {
        if (!this.service.exist(updateParam.getRoleId())) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        this.roleUserApi.bindUserRole(updateParam.getRoleId(), updateParam.getUserIds());
        return R.ok();
    }

    @Override
    @OperationLog(value = "移除角色用户")
    public R<Object> removeRoleUser(RoleUserUpdateParamDTO updateParam) {
        if (!this.service.exist(updateParam.getRoleId())) {
            throw new FrameworkException(RoleExceptionInfo.ROLE_NOT_FOUND);
        }
        this.roleUserApi.unbindUserRole(updateParam.getRoleId(), updateParam.getUserIds());
        return R.ok();
    }

    @Override
    public R<List<RoleDTO>> listByIds(BatchParam<String> ids) {
        if (Tools.Coll.isBlank(ids.getList())) {
            return R.ok(new ArrayList<>());
        }
        return R.ok(this.service.modelConvert(this.service.listByIds(ids.getList())));
    }

    @Override
    public R<Map<String, RoleDTO>> mapByIds(BatchParam<String> ids) {
        if (Tools.Coll.isBlank(ids.getList())) {
            return R.ok(new HashMap<>());
        }
        return R.ok(Tools.Coll.convertMap(this.service.modelConvert(this.service.listByIds(ids.getList())), RoleDTO::getId));
    }

    @Override
    public R<Map<String, List<String>>> mapMenuIdsByIds(BatchParam<String> roleIds) {
        return R.ok(Tools.Coll.convertGroup(this.roleMenuService.listByRoleIds(roleIds.getList()), RoleMenuDO::getRoleId, RoleMenuDO::getMenuId));
    }

    @Override
    public R<List<RoleDTO>> listByName(String name) {
        return R.ok(this.service.modelConvert(this.service.listByName(name, Me.domainCode(), Me.tenantCode(), Me.appCode())));
    }

    @Override
    public R<Object> drag(DragOrderParam dragParam) {
        DragHelper<RoleDO> dragHelper = DragHelperBuilder.builder(this.service::between)
                .which(dragParam.getParentId(), dragParam.getTargetParentId(), DragTargetPositionEnum.INNER.equals(dragParam.getPosition()))
                .currentNode(() -> this.service.getById(dragParam.getCurrentId()))
                .targetNode(() -> this.service.getById(dragParam.getTargetId()))
                .maxSortOrder(this.service::getMaxSortOrder)
                .beforeTarget(DragTargetPositionEnum.BEFORE.equals(dragParam.getPosition()))
                .build();
        this.service.updateBatchById(dragHelper.drag());
        return R.ok();
    }

    @EventListener
    public void deleteTenantEventListener(TenantDeleteEvent event) {
        this.service.removeByTenantCode(event.getCode());
        this.roleMenuService.remove(Wrappers.<RoleMenuDO>lambdaQuery().eq(RoleMenuDO::getTenantCode, event.getCode()));
    }
}
